package org.example.service.core;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.config.MinioConfig;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class AppiumExecute {

   private MinioConfig minioConfig;

   private MinioClient minioClient;

   private UiAutomator2Options options;

   public AppiumExecute(MinioConfig minioConfig, MinioClient minioClient, UiAutomator2Options options) {
      this.minioConfig = minioConfig;
      this.minioClient = minioClient;
      this.options = options;
   }
   public String execute(String url){
      // 创建AppiumDriver对象，连接上Appium服务器，并将相应的键值对传过去
      AndroidDriver driver = null;
      String downloadedFile = null;

      try {
         driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), options);

//            Thread.sleep(3000);

         driver.findElement(By.id("com.android.browser:id/url")).click();
         driver.findElement(By.className("android.widget.EditText")).sendKeys(url);

         // 使用 Actions 类发送回车
         Actions actions = new Actions(driver);
         actions.sendKeys(Keys.ENTER).perform();

         // 使用显式等待来定位并点击元素
         WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 最长等待时间为10秒
         wait.until(
                 ExpectedConditions.elementToBeClickable(By.xpath("//*[@text='安装']")) // 请根据实际情况调整元素类型
         ).click();


         // 监控下载目录，获取最新下载的文件
         String downloadDir = "/sdcard/Download";
         Thread.sleep(5000);
         downloadedFile = waitForDownloadToComplete(downloadDir);

         if (downloadedFile != null) {
            // 复制文件到本地
            String localFilePath = "D:\\dcloud-software\\software\\static\\" + downloadedFile;
            copyFileFromEmulatorToLocal(downloadDir + "/" + downloadedFile, localFilePath);
            uploadFileToMinIO(localFilePath);
            //返回值 downloadedFile  TODO
         } else {
            log.error("下载文件未完成或未找到！");
         }

      } catch (Exception e) {
         log.error("Appium异常, 测试用例不通过!", e);
         throw new RuntimeException(e);
      }

      return downloadedFile;
   }

   /**
    * 等待下载到指定目录的文件完成。
    * 通过检查文件是否被锁定来判断下载是否完成。
    * 如果文件在最大重试次数内未解锁，则认为下载失败。
    *
    * @param downloadDir 下载文件的目标目录。
    * @return 完成下载的文件名，如果下载失败则返回null。
    * @throws InterruptedException 如果线程被中断。
    */
   // 定时检查下载目录中的文件，直到文件下载完成
   private static String waitForDownloadToComplete(String downloadDir) throws InterruptedException {
      // 初始化重试计数和最大重试次数
      int retryCount = 0;
      int maxRetries = 100; // 最大重试次数
      int retryInterval = 5000; // 每次重试间隔，单位：毫秒

      List<String> files = null;
      String currentFileName = null;
      try {
         // 列出目录中最近修改的文件列表
         //获得文件列表
         files = listFilesInDirectoryByTime(downloadDir);
         for (String file : files) {
            log.info("列表文件名：{}", file);
         }
         // 检查文件列表是否为空，抛出异常以避免后续操作
         if (files.get(1) == null) {
            throw new RuntimeException("空指针异常");
         }
         // 获取第二个文件名，假设它是正在下载的文件
         currentFileName = files.get(1);
         log.info("正在下载文件名：{}", currentFileName);
      } catch (IOException e) {
         log.error("列出目录文件时出错", e);
      }

      // 循环检查文件是否解锁，直到重试次数达到最大值
      while (retryCount < maxRetries) {
         // 如果文件未被锁定，则下载完成
         if (!isFileLocked(downloadDir + "/" + currentFileName, currentFileName)) {
            log.error("文件下载完成");
            return currentFileName;
         } else {
            // 如果文件被锁定，重试计数增加，并等待一段时间后再次检查
            retryCount++;
            Thread.sleep(retryInterval);
            log.error("文件下载未完成");
         }
      }
      // 如果达到最大重试次数仍未下载完成，则返回null
      return null;
   }

   /**
    * 列出指定目录中的文件，按修改时间排序。
    * 通过执行ADB shell命令来获取文件列表。
    *
    * @param directory 目标目录。
    * @return 目录中文件的列表。
    * @throws IOException 如果执行ADB命令时发生错误。
    */
   private static List<String> listFilesInDirectoryByTime(String directory) throws IOException {
      // 执行ADB shell ls命令来列出目录中的文件
      Process process = Runtime.getRuntime().exec(new String[]{"adb", "shell", "ls", "-lt", directory});
      BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
      String line;
      List<String> files = new ArrayList<>();
      while ((line = reader.readLine()) != null) {
         // 解析命令输出，获取文件名
         // 解析行，获取文件名
         String[] parts = line.split("\\s+");
         if (parts.length > 0) {
            String fileName = parts[parts.length - 1];
            files.add(fileName);
         }
      }
      reader.close();
      return files;
   }

   /**
    * 从模拟器中复制文件到本地路径。
    * 使用ADB pull命令执行文件复制。
    *
    * @param remoteFilePath 模拟器中的文件路径。
    * @param localFilePath  本地目标文件路径。
    */
   private static void copyFileFromEmulatorToLocal(String remoteFilePath, String localFilePath) {
      try {
         // 执行ADB pull命令来复制文件
         String[] command = {"adb", "pull", remoteFilePath, localFilePath};
         Process process = Runtime.getRuntime().exec(command);
         BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
         String line;
         while ((line = reader.readLine()) != null) {
            System.out.println(line);
         }
         process.waitFor();
         reader.close();
      } catch (Exception e) {
         log.error("复制文件失败!", e);
      }
   }

   private void uploadFileToMinIO(String downloadFilePath) {
      String filename = downloadFilePath.substring(downloadFilePath.lastIndexOf("\\") + 1);
      log.info("文件名：{}", filename);
      Path path = Paths.get(downloadFilePath);
      try (FileInputStream fileInputStream = new FileInputStream(downloadFilePath)) {
//            Process downloadProcess = Runtime.getRuntime().exec(new String[]{"adb", "pull", downloadFilePath, "-"});
//            InputStream inputStream = downloadProcess.getInputStream();
         long fileSize = Files.size(path);
         log.error("-------------没问题");
//            long fileSize = getFileSize(downloadFilePath);
         minioClient.putObject(
                 PutObjectArgs.builder()
                         .bucket(minioConfig.getBucketName()) // 指定存储桶名称
                         .object(filename) // 指定对象（文件）名
                         .stream(fileInputStream, fileSize, -1) // 文件输入流和文件大小
                         .contentType("application/vnd.android.package-archive") // 自动获取文件的 MIME 类型
                         .build()
         );

//            fileInputStream.close();
         System.out.println("文件上传到 MinIO 成功: " + downloadFilePath);
      } catch (IOException e) {
         System.err.println("666读取本地文件时发生错误: " + e);
      } catch (Exception e) {
         System.err.println("上传到 MinIO 时发生错误: " + e.getMessage());
      }
   }


   /**
    * 检查指定路径的文件是否被锁定。
    * 通过执行ADB shell lsof命令来检查文件是否被锁定。
    *
    * @param filePath 文件的完整路径。
    * @param fileName 文件名。
    * @return 如果文件被锁定，则返回true；否则返回false。
    */
   private static boolean isFileLocked(String filePath, String fileName) {
      Process process;
      try {
         // 执行ADB shell lsof命令来检查文件是否被锁定
         process = Runtime.getRuntime().exec(new String[]{"adb", "shell", "lsof", filePath});
         BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
         String line;
         while ((line = reader.readLine()) != null) {
            // 如果命令输出中包含文件名，则文件被锁定
            System.out.println("hhhhh" + line);
            if (line.contains(fileName)) {
               return true; // 文件被锁定
            }
         }
      } catch (IOException e) {
         log.error("检查文件锁定状态时出错", e);
      }
      return false;
   }

}
